<?php

namespace Cekurte;
	
/**
 * Classe responsável pelo acesso ao banco de dados da aplicação
 * 
 * @author 	João Paulo Cercal
 * @version 1.0
 */
abstract class Model
{
	/**
	 * @var string namespace que no qual o doctrine2 foi configurado
	 */
	const ENTITY_NAMESPACE 		= 'Entities';
	
	/**
	 * @var string alias para consultas no banco de dados
	 */
	const ENTITY_ALIAS 			= 'entity';
	
	/**
	 * @var string ordenação dos resultados em ordem ascendente
	 */
	const ORDER_BY_ASC 			= 'ASC';
	
	/**
	 * @var string ordenação dos resultados em ordem descendente
	 */
	const ORDER_BY_DESC 		= 'DESC';
	
	/**
     * @var EntityManager Gerenciador de entidades (Doctrine 2)
     */
	protected $em				= null;
	
	/**
     * @var string Entidade
     */
	protected $entity			= null;
	
	/**
     * @var Form um formulário
     */
	protected $form				= null;
	
	/**
     * @var Armazena o Action Helper FlashMessenger
     */
	protected $flashMessenger	= null;
	
	/**
	 * Método Construtor
	 * 
	 * @author 	João Paulo Cercal
     * @version 1.0
	 */
	public function __construct()
	{
		$this->em 				= \Zend_Registry::getInstance()->get('entityManager');
		
		$this->flashMessenger	= \Zend_Controller_Action_HelperBroker::getExistingHelper('FlashMessenger');
		
		$this->setup();
	}
	
	/**
     * Este método é chamado automaticamente a cada requisição pelo método construtor().
     * Dessa forma quando se faz necessário adicionar algum comportamento na inicialização
     * de um modelo a classe filha deve implementar esse método.
     * 
     * @author 	João Paulo Cercal
     * @version 1.0
     */
	protected function setup() { }
	
	/**
     * Pega o id do usuário registrado na aplicação
     * 
     * @author 	João Paulo Cercal
     * @version 1.0
     * 
     * @return 	int id do usuário
     */
    protected function getUserId()
    {
    	return \Zend_Auth::getInstance()->getIdentity()->user->id;
    }
    
    /**
	 * Procura um registro pela chave primária
	 * 
	 * @param 	mixed $id chave primária
	 * 
	 * @author 	João Paulo Cercal
     * @version 1.0
	 */
	public function findById( $id )
	{
		$registro = $this->find( array(), array('id' => $id) );
		
		return $registro[0];
	}
	
	/**
	 * Pega um registro pela chave primária
	 * 
	 * @param 	mixed $id chave primária
	 * 
	 * @author 	João Paulo Cercal
     * @version 1.0
	 */
	public function getRegistry( $id )
	{
		return $this->em->find( self::ENTITY_NAMESPACE . '\\' . $this->entity , $id );
	}
	
	/**
	 * Procura registros
	 * 
	 * @param 	array $select campos a serem selecionados na consulta SQL, e retornados pelo método find()
	 * @param 	array $where condições para a pesquisa SQL, monta a clausula WHERE. Deverá ser composta por um array contendo
	 * chave e valor. A chave é a coluna do banco de dados. O valor é o filtro.
	 * @param 	array $orderBy Ordena os resultados. A chave é o nome da coluna, o valor deve ser uma constante da classe Model ORDER_BY_ASC ou ORDER_BY_DESC
	 * @param 	array $groupBy Agrupa os resultados. Seu valor deve ser o nome da coluna.
	 * 
	 * <code>
	 *     $registry = $user->find( array('username'), array('id' => 1), array( 'id' => Model::ORDER_BY_DESC ), array('password') );
	 * </code>
	 * 
	 * Explicação:
	 * Pega a coluna nome, 
	 * onde o id seja igual a 1, 
	 * ordena os resultados pela coluna id em ordem decrescente, 
	 * agrupa os resultados pela coluna password
	 * 
	 * @author 	João Paulo Cercal
     * @version 1.0
	 */
	public function find( array $select = array(), array $where = array(), array $orderBy = array(), array $groupBy = array() )
	{
		$queryBuilder = $this->em->createQueryBuilder();
		
		if( empty( $select ) )
		{
			$queryBuilder->select( self::ENTITY_ALIAS );
		}
		else
		{
			foreach( $select as $value )
			{
				$queryBuilder->addSelect( self::ENTITY_ALIAS . '.' . $value );
			}
		}
		
		$queryBuilder->from( self::ENTITY_NAMESPACE . '\\' . $this->entity , self::ENTITY_ALIAS );
		
		if( !empty( $where ) )
		{
			foreach( $where as $key => $value )
			{
				$queryBuilder->andWhere( self::ENTITY_ALIAS . ".{$key} = :{$key}"  );
				
				$queryBuilder->setParameter( $key, $value );
			}
		}
		
		if( !empty( $orderBy ) )
		{
			foreach( $orderBy as $key => $value )
			{
				$queryBuilder->addOrderBy( self::ENTITY_ALIAS . '.' . $key, $value );
			}
		}
		
		if( !empty( $groupBy ) )
		{
			foreach( $groupBy as $key )
			{
				$queryBuilder->addGroupBy( self::ENTITY_ALIAS . '.' . $key );
			}
		}
		
		$query = $queryBuilder->getQuery();
		
		return $query->getArrayResult();
	}
	
	/**
	 * Salva ou atualiza um registro
	 * 
	 * @param 	array $data dados que deverão ser inseridos ou atualizados.
	 * @param 	mixed $id valor da chave primária do registro. Se informado o método save() irá 
	 * criar um SQL para inserir informações no banco de dados, do contrário irá criar um comando
	 * para realizar a atualização do registro tendo como base o id aqui informado.
	 * 
	 * @return 	boolean true se inserir ou atualizar, do contrário, false.
	 * 
	 * @author 	João Paulo Cercal
     * @version 1.0
	 */
	public function save( array $data, $id = null )
	{
		$entity	= self::ENTITY_NAMESPACE . '\\' . $this->entity;
	
		if( empty( $id ) )
		{
			$registry = new $entity();
		}
		else
		{
			$registry = $this->getRegistry( $id );
		}
		
		unset( $data['token'] );
		unset( $data['id'] );
		unset( $data['submit'] );
		
		try 
		{	
			foreach( $data as $key => $value )
			{
				$columnParts = explode('_', $key);
				
				foreach( $columnParts as $part )
				{
					$column = ucfirst( $part );
				}
				
				$method = "set{$column}";
				
				if( !method_exists($registry, $method) )
				{
					throw new \Exception("O método {$method}() não existe!");	
				}
				
				$registry->{$method}( $value );
			}
				
			$this->em->persist( $registry );
            
            $this->em->flush();
            
            return true;
		}
		catch( \Exception $e )
        {
            $this->flashMessenger->addMessage( $e->getMessage() );
            
            return false;
		}
	}
	
	public function delete( array $where )
	{
		$this->em->getConnection()->beginTransaction();
		
        try
        {
            $data = $this->em->getRepository( self::ENTITY_NAMESPACE . '\\' . $this->entity )->findBy( $where );
            
            if( empty( $data ) )
            {
            	throw new \Exception('A condicao where nao encontrou nenhum registro!');
            }
            else
            {	
            	foreach( $data as $registry )
            	{
            		$this->em->remove( $registry );
            	}
            }
            
            $this->em->flush();
            $this->em->getConnection()->commit();
            
            return true;
        } 
        catch( \Exception $e )
        {
            $this->em->close();
            $this->em->getConnection()->rollback();
            
            $this->flashMessenger->addMessage( $e->getMessage() );
            
            return false;
        }
	}
}

?>
